﻿// <copyright file="UserStarter.cs" company="Ecole Normale Supérieure">
// Released under GPLv2
// </copyright>
// <summary>Contains the UserStarter class.</summary>

namespace Sqwarea.GameLogic
{
    using System;
    using System.Drawing;
    using Sqwarea.GameLogic.Extensions;
    using Sqwarea.Persistence;

    /// <summary>
    /// This class is used to start with a User : start position, creation, etc.
    /// </summary>
    public class UserStarter : IUserStarter
    {
        /// <summary>
        /// The game provider.
        /// </summary> 
        private readonly IGameProvider _gameProvider;

        /// <summary>
        /// The user provider.
        /// </summary>
        private readonly IUserProvider _userProvider;

        /// <summary>
        /// The constant provider.
        /// </summary>
        private readonly IGlobalVariables _globals;

        /// <summary>
        /// Initializes a new instance of the UserStarter class. 
        /// </summary>
        /// <param name="gameProvider">The game Provider</param>
        /// <param name="globals">The constant provider.</param>
        /// <param name="userProvider">The user provider.</param>
        public UserStarter(IGameProvider gameProvider, IGlobalVariables globals, IUserProvider userProvider)
        {
            if (null == gameProvider)
            {
                throw new ArgumentNullException("gameProvider", "UserStarter");
            }

            if (null == globals)
            {
                throw new ArgumentNullException("globals", "UserStarter");
            }

            if (null == userProvider)
            {
                throw new ArgumentNullException("userProvider", "UserStarter");
            }

            this._gameProvider = gameProvider;
            this._globals = globals;
            this._userProvider = userProvider;
        }

        /// <summary>
        /// The main thing : creates a new User ready to play.
        /// Notice that when a king is dead, we create a new king and we do not clean the squares of the previous king.
        /// </summary>
        /// <param name="user">user to update</param>
        /// <returns>The created king</returns>
        public King CreateNewUser(User user)
        {
            const int SoldiersOnSquare = GlobalParameters.InitialSquareTroopsNumber;
            const int SoldiersInAcademy = GlobalParameters.InitialAcademyTroopsNumber;

            /* Main computation in this part.
            */
            var position = this.ComputeStartPoint();
            long id = this._gameProvider.GetNextKingId();

            /* filling the lacking fields in user.
             */
            user.Id = id;
            this._userProvider.UpdateUser(user);

            /* Creating a new king.
             * All fields are ok.
             */
            var king = new King(id, user.OpenId, user.Nickname)
                            {
                                AcademyTroops = SoldiersInAcademy,
                                LastAcademyTroopsUpdate = DateTime.UtcNow,
                                LastKingMoveDate = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero),
                                Position = position,
                                SquaresNumber = GlobalParameters.InitialSquareCount,
                                SquaresRectangle = new Rectangle(position.X - 1, position.Y - 1, 3, 3)
                            };
            this._gameProvider.UpdateKing(king);

            /* Here we declare the 4 adjacent squares are hims.
             * + the central one.
             */

            this._gameProvider.UpdateSquare(new Square(
                                                position,
                                                id,
                                                SoldiersOnSquare,
                                                true));

            foreach (Direction dir in new Directions())
            {
                this._gameProvider.UpdateSquare(new Square(
                                                    position.GetDir(dir),
                                                    id,
                                                    SoldiersOnSquare,
                                                    false)
                                                    {
                                                        PathToKing = Directions.Opposite(dir)
                                                    });
            }

            return king;
        }

        /// <summary>
        /// States whether the zone around (x,y)  is safe to install a new User
        /// </summary>
        /// <param name="x"> x coordinate.</param>
        /// <param name="y">y coordinate.</param>
        /// <returns> yes or no. </returns>
        public bool ValidZone(int x, int y) // should be private
        {
            Square[,] zone = this._gameProvider.GetSquaresInRectangle(
                new Rectangle(x - 5, y - 5, 11, 11));

            foreach (var square in zone)
            {
                // Some kings have a KingId!=NeutralKingId but are neutral
                // It's not very important, it may just keep some places having new users
                if (King.NeutralKingId != square.KingId || square.Troops != Square.DefaultTroopsNumber)
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Determines a startpoint for a new User.
        /// </summary>
        /// <returns>The StartPoint.</returns>
        public Point ComputeStartPoint() // should be private
        {
            uint r = this._globals.NewKingsRadius; // remplacer par 0 pour les tests. ;
            while (true)
            {
                for (int i = 0; i < 10; i++)
                {
                    double alpha = Tools.RandDouble() * System.Math.PI * 2;
                    var x = (int)(r * System.Math.Cos(alpha));
                    var y = (int)(r * System.Math.Sin(alpha));
                    if (this.ValidZone(x, y))
                    {
                        return new Point(x, y);
                    }
                }

                r++;
                this._globals.NewKingsRadius++;
            }
        }
    }
}
